package cloud

import (
	"encoding/json"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	Log "github.com/cihub/seelog"
	"github.com/gin-gonic/gin"
	"gopkg.in/ini.v1"
)

var (
	appCfg             *ini.File //配置文件变量
	glbAppiCloudApp    = 0       //是否要加入分布式系统(0:待初始化;1:是;2:否)
	glbAppiCloudSystem = 0       //是否要启用桥接(0:待初始化;1:是;2:否)
	once               sync.Once
)

/**
 * 初始化
 */
func init() {
	//RegistraCloud()
}

/**
 * 注册服务到注册中心
 */
func RegistraCloud() {
	if !IsCloudServe() {
		return
	}

	// go runUdpServe()
	go registraCloudHttp()
	go registraCloudUdp()
}

/**
 * 注册服务到注册中心-UDP方式
 */
func registraCloudUdp() {
	cloudCenter := readConfigKey("CloudCenter", "Site", "")
	if cloudCenter == "" || strings.HasPrefix(cloudCenter, "http://") {
		return
	}

	cloudCenterUser := readConfigKey("CloudCenter", "User", "")
	cloudCenterPassword := readConfigKey("CloudCenter", "Password", "")
	DomainName := readConfigKey("System", "DomainName", "")
	DomainPort := readConfigKey("System", "DomainPort", "")

	Port := readConfigKey("System", "Port", "8080")
	Name := readConfigKey("System", "Name", "")

	var builder strings.Builder
	builder.WriteString("{\"sign\":\"")
	builder.WriteString(Name)
	builder.WriteString("Server\",\"serverPort\":\"")
	builder.WriteString(Port)
	builder.WriteString("\",\"serverIp\":\"unknown\",\"domainName\":\"")
	builder.WriteString(DomainName)
	builder.WriteString("\",\"domainPort\":\"")
	builder.WriteString(DomainPort)
	builder.WriteString("\",\"pid\":\"")
	builder.WriteString(strconv.Itoa(os.Getpid()))
	builder.WriteString("\"}")

	params := map[string]string{}
	params["user"] = cloudCenterUser
	params["key"] = cloudCenterPassword
	params["sign"] = Name + "Server"
	params["registerTxt"] = url.QueryEscape(builder.String())
	params["pid"] = strconv.Itoa(os.Getpid())

	//appCfg = nil //释放

	cloudCenter = strings.TrimPrefix(cloudCenter, "udp://")

	udpAddr, err := net.ResolveUDPAddr("udp", cloudCenter)
	if err != nil {
		Log.Error("注册服务IP错误:", cloudCenter)
		return
	}

	udpTxt, _ := json.Marshal(params)
	sendData := []byte(udpTxt)

	for {
		socket, err := net.DialUDP("udp", nil, udpAddr)

		if err != nil {
			time.Sleep(time.Second * 5)
			continue
		}

		defer socket.Close()

		_, err = socket.Write(sendData) // 发送注册数据
		if err != nil {                 //失败了不用管信息
			time.Sleep(time.Second * 5)
			continue
		}

		data := make([]byte, 4096)
		n, _, err := socket.ReadFromUDP(data) // 接收数据
		if err != nil {
			Log.Error("接收数据失败, err: ", err)
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		if n <= 0 {
			Log.Info("注册服务返回信息为空,不是预期结构内容")
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		me := map[string]interface{}{}
		err = json.Unmarshal(data[:n], &me)
		if err != nil {
			Log.Error("Json字符串转换异常:", err)
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		if !me["success"].(bool) {
			Log.Error("注册服务失败,返回内容:", me["msg"])
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		//fmt.Println("注册服务成功")
		Log.Info("注册服务成功")
		return
	}
}

/**
 * 注册服务到注册中心-Http方式
 */
func registraCloudHttp() {
	cloudCenter := readConfigKey("CloudCenter", "Site", "")
	if cloudCenter == "" || !strings.HasPrefix(cloudCenter, "http://") {
		return
	}

	cloudCenterUser := readConfigKey("CloudCenter", "User", "")
	cloudCenterPassword := readConfigKey("CloudCenter", "Password", "")
	DomainName := readConfigKey("System", "DomainName", "")
	DomainPort := readConfigKey("System", "DomainPort", "")

	Port := readConfigKey("System", "Port", "8080")
	Name := readConfigKey("System", "Name", "")

	var builder strings.Builder
	builder.WriteString("{\"sign\":\"")
	builder.WriteString(Name)
	builder.WriteString("Server\",\"serverPort\":\"")
	builder.WriteString(Port)
	builder.WriteString("\",\"serverIp\":\"unknown\",\"domainName\":\"")
	builder.WriteString(DomainName)
	builder.WriteString("\",\"domainPort\":\"")
	builder.WriteString(DomainPort)
	builder.WriteString("\"}")

	var host strings.Builder
	host.WriteString("http://")
	host.WriteString(cloudCenter)
	host.WriteString("/register/add")

	params := url.Values{}
	params.Add("user", cloudCenterUser)
	params.Add("key", cloudCenterPassword)
	params.Add("sign", Name+"Server")
	params.Add("registerTxt", url.QueryEscape(builder.String()))

	//appCfg = nil //释放
	urlStr := strings.Replace(host.String(), "http://http://", "http://", 1)

	for {
		bl, errStr := httpGet(urlStr, params)
		if !bl {
			Log.Error("注册服务失败,返回内容:", errStr)
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		if errStr == "" {
			Log.Info("注册服务返回信息为空,不是预期结构内容")
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		me := map[string]interface{}{}
		err := json.Unmarshal([]byte(errStr), &me)
		if err != nil {
			Log.Error("Json字符串转换异常:", err)
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		if !me["success"].(bool) {
			Log.Error("注册服务失败,返回内容:", me["msg"])
			time.Sleep(time.Second * 5) //休眠5秒
			continue
		}

		//fmt.Println("注册服务成功")
		Log.Info("注册服务成功")
		return
	}
}

// GET请求
func httpGet(urlStr string, params url.Values) (bool, string) {
	paramsStr := params.Encode()
	if paramsStr != "" {
		paramsStr = "?" + paramsStr
	}

	urlStr = urlStr + paramsStr // 构造完整的 URL

	// 发送 GET 请求
	resp, err := http.Get(urlStr)
	if err != nil {
		Log.Error("GET请求URL发生异常:", err)
		return false, err.Error()
	}
	defer resp.Body.Close()

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		Log.Error("读取响应体发生异常:", err)
		return false, string(body)
	}

	return true, string(body)
}

// #region @Api {title=中心监听接口}
// @return {type=json explainType=(bool, string, interface{}) explain=返回响应}
// @RequestMapping {name=ServerIp type=GET value=/cloud/monitor}
// #endregion
func CloudMonitor(ctx *gin.Context) (bool, string, interface{}) {
	return true, "访问成功", time.Now().Format("2006-01-02 15:04:05")
}

/**
 * 判断是否是分布式系统
 */
func IsCloudServe() bool {
	if glbAppiCloudApp != 0 {
		return glbAppiCloudApp == 1
	}

	if !(hasSection("CloudCenter")) {
		glbAppiCloudApp = 2
		return false
	}

	glbAppiCloudApp = 1
	return true
}

// 判断程序是否启用桥接
func IsCloudSystem() bool {
	if glbAppiCloudSystem != 0 {
		return glbAppiCloudSystem == 1
	}

	if hasSection("CloudSystem") {
		glbAppiCloudSystem = 1
		return true
	}

	glbAppiCloudSystem = 2
	return false
}

// 判断配置组是否存在
func hasSection(sectionName string) bool {
	iniCfg()

	section, _ := appCfg.GetSection(sectionName) // return appCfg.HasSection(sectionName)//这种方式不安全

	if section == nil {
		return false
	}

	if len(section.Keys()) == 0 {
		return false
	}

	return true
}

// 初始化配置文件变量
func iniCfg() {
	once.Do(func() {
		root := ""
		exePath, err := os.Executable()
		if err != nil {
			root = "."
		}

		root, _ = filepath.EvalSymlinks(filepath.Dir(exePath))

		configFilePath := strings.Replace(root+"/config/app.ini", "\\", "/", -1)

		_, err = os.Stat(configFilePath) //os.Stat获取文件信息
		if err != nil {
			if !os.IsExist(err) {
				Log.Error("配置文件不存在:", err)
				return
			}
		}

		appCfg, err = ini.Load(configFilePath)
		if err != nil {
			Log.Error("配置文件读取错误:", err)
			return
		}
	})
}

// 读取配置值
func readConfigKey(section, key, def string) string {
	title := appCfg.Section(section)
	value, _ := title.GetKey(key)
	if value == nil {
		return def
	}

	return strings.TrimSpace(value.String())
}

/**
 * 启动UDP服务
 */
func runUdpServe() {
	Port := readConfigKey("System", "Port", "8080")
	appInsideKey := readConfigKey("App", "InsideKey", "")

	iPort, _ := strconv.Atoi(Port)
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: iPort,
	})

	if err != nil {
		Log.Error("开启UDP服务失败:", err)
		return
	}
	defer listen.Close()

	for {
		var data [1024]byte
		n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
		if err != nil {
			Log.Error("读取UDP信息失败:", err)
			continue
		}

		msg := string(data[:n])

		if msg == "ping" {
			msg = strings.Replace(`{"success":true,"msg":"访问成功","data":"@time"}`, "@time", time.Now().Format("2006-01-02 15:04:05"), -1)
			listen.WriteToUDP([]byte(msg), addr) // 回复数据
			continue
		}

		if strings.HasPrefix(msg, "shutdown_") && appInsideKey != "" {
			insidekey := strings.TrimPrefix(msg, "shutdown_")
			if insidekey == "" {
				msg = `{"success": false, "msg": "关闭密钥参数insidekey缺失", "data": 100001}`
				listen.WriteToUDP([]byte(msg), addr) // 回复数据
				continue
			}

			if appInsideKey != insidekey {
				msg = `{"success": false, "msg": "关闭密钥参数错误", "data": 100002}`
				listen.WriteToUDP([]byte(msg), addr) // 回复数据
				continue
			}

			Log.Debug("关闭服务...")

			msg = `{"success": true, "msg": "服务关闭请求已接收", "data": 9999}`
			listen.WriteToUDP([]byte(msg), addr) // 回复数据

			os.Exit(0)
			return
		}
	}

}
